#include "RCGraphicsDevice.h"

RCGraphicsDevice::RCGraphicsDevice(void){ }


RCGraphicsDevice::~RCGraphicsDevice(void)
{ 
	if(pImmediateContext)
	{
		pImmediateContext->ClearState();
		pImmediateContext->Flush();
	}

	pRasterState->Release();
	pSwapChain->Release();
	pRenderTargetView->Release();
	pDepthStencilView->Release();
	pDepthStencil->Release();
	pd3dDevice->Release();
	pImmediateContext->Release();
}

// need to link d3d11.lib
HRESULT RCGraphicsDevice::Initialize()
{
	HRESULT hr = S_OK;

	RECT rc;
    GetClientRect( hWnd, &rc );
    UINT width = rc.right - rc.left;
    UINT height = rc.bottom - rc.top;

	UINT createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_DRIVER_TYPE driverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
	UINT numDriverTypes = ARRAYSIZE( driverTypes );

	D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };
    UINT numFeatureLevels = ARRAYSIZE( featureLevels );

	DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory( &sd, sizeof( sd ) );
    sd.BufferCount = 1;
    sd.BufferDesc.Width = width;
    sd.BufferDesc.Height = height;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = hWnd;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;

	for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
    {
        driverType = driverTypes[driverTypeIndex];
        hr = D3D11CreateDeviceAndSwapChain( NULL, driverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels,
                                            D3D11_SDK_VERSION, &sd, &pSwapChain, &pd3dDevice, &featureLevel, &pImmediateContext );
        if( SUCCEEDED( hr ) )
            break;
    }
    if( FAILED( hr ) )
        return hr;

	// Create a render target view
    ID3D11Texture2D* pBackBuffer = NULL;
    hr = pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
    if( FAILED( hr ) )
        return hr;

    hr = pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &pRenderTargetView );
    pBackBuffer->Release();

    if( FAILED( hr ) )
        return hr;

	// Create depth stencil texture
    D3D11_TEXTURE2D_DESC descDepth;
    ZeroMemory( &descDepth, sizeof(descDepth) );
    descDepth.Width = width;
    descDepth.Height = height;
    descDepth.MipLevels = 1;
    descDepth.ArraySize = 1;
    descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    descDepth.SampleDesc.Count = 1;
    descDepth.SampleDesc.Quality = 0;
    descDepth.Usage = D3D11_USAGE_DEFAULT;
    descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    descDepth.CPUAccessFlags = 0;
    descDepth.MiscFlags = 0;
    hr = pd3dDevice->CreateTexture2D( &descDepth, NULL, &pDepthStencil );
    if( FAILED( hr ) )
        return hr;

    // Create the depth stencil view
    D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
    ZeroMemory( &descDSV, sizeof(descDSV) );
    descDSV.Format = descDepth.Format;
    descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
    descDSV.Texture2D.MipSlice = 0;
    hr = pd3dDevice->CreateDepthStencilView( pDepthStencil, &descDSV, &pDepthStencilView );
    if( FAILED( hr ) )
        return hr;

	D3D11_RASTERIZER_DESC rasterDesc;
	rasterDesc.AntialiasedLineEnable = false;
	rasterDesc.CullMode = D3D11_CULL_BACK;
	//rasterDesc.CullMode = D3D11_CULL_NONE;
	rasterDesc.DepthBias = 0;
	rasterDesc.DepthBiasClamp = 0.0f;
	rasterDesc.DepthClipEnable = true;
	rasterDesc.FillMode = D3D11_FILL_SOLID;
	rasterDesc.FrontCounterClockwise = false;
	rasterDesc.MultisampleEnable = false;
	rasterDesc.ScissorEnable = false;
	rasterDesc.SlopeScaledDepthBias = 0.0f;

	// Create the rasterizer state from the description we just filled out.
	hr = pd3dDevice->CreateRasterizerState(&rasterDesc, &pRasterState);
	if(FAILED(hr))
	{
		return hr;
	}

	// Now set the rasterizer state.
	pImmediateContext->RSSetState(pRasterState);

	// Setup the viewport    
    viewPort.Width = (FLOAT)width;
    viewPort.Height = (FLOAT)height;
    viewPort.MinDepth = 0.0f;
    viewPort.MaxDepth = 1.0f;
    viewPort.TopLeftX = 0;
    viewPort.TopLeftY = 0;
    pImmediateContext->RSSetViewports( 1, &viewPort );

    pImmediateContext->OMSetRenderTargets( 1, &pRenderTargetView, pDepthStencilView );
	
	return S_OK;
}

D3D_DRIVER_TYPE RCGraphicsDevice::getDriverType()
{
	return driverType;
}
D3D_FEATURE_LEVEL RCGraphicsDevice::getFeatureLevel()
{
	return featureLevel;
}

ID3D11RasterizerState* RCGraphicsDevice::getRasterizer()
{
	return pRasterState;
}
IDXGISwapChain* RCGraphicsDevice::getSwapChain()
{
	return pSwapChain;
}
ID3D11RenderTargetView* RCGraphicsDevice::getRenderTerget()
{
	return pRenderTargetView;
}
ID3D11DepthStencilView* RCGraphicsDevice::getDepthStencilView()
{
	return pDepthStencilView;
}
D3D11_VIEWPORT RCGraphicsDevice::getViewPort()
{
	return viewPort;
}
ID3D11Texture2D* RCGraphicsDevice::getDepthStencil()
{
	return pDepthStencil;
}
ID3D11Device* RCGraphicsDevice::getDevice()
{
	return pd3dDevice;
}
ID3D11DeviceContext* RCGraphicsDevice::getDeviceContext()
{
	return pImmediateContext;
}
HWND RCGraphicsDevice::getWindowHandle()
{
	return hWnd;
}

void RCGraphicsDevice::Clear(XMFLOAT4 Color, UINT clearFlag,FLOAT depth, UINT8 stencil)
{
	//
	// Clear the back buffer
	//
	float ClearColor[4] = { Color.x, Color.y, Color.z, Color.w }; // red, green, blue, alpha
	pImmediateContext->ClearRenderTargetView( pRenderTargetView, ClearColor );

	//
	// Clear the depth buffer to 1.0 (max depth)
	//
	pImmediateContext->ClearDepthStencilView( pDepthStencilView, clearFlag, depth, stencil );
}
void RCGraphicsDevice::Present(UINT sysncInterval, UINT flags)
{
	//
    // Present our back buffer to our front buffer
    //
    pSwapChain->Present( sysncInterval, flags );
}

HWND RCGraphicsDevice::InitializeWindow(void** game, LPCTSTR str_Title,int int_XPos, int int_YPos, int int_Width, int int_Height, WNDPROC WinProc, int colorBrush)
{
	// Register class
    WNDCLASSEX wnd_Structure;

	wnd_Structure.cbSize = sizeof(WNDCLASSEX);
	wnd_Structure.style = CS_HREDRAW | CS_VREDRAW;
	wnd_Structure.lpfnWndProc = WinProc;
	wnd_Structure.cbClsExtra = 0;
	wnd_Structure.cbWndExtra = 0;
	wnd_Structure.hInstance = GetModuleHandle(NULL);
	wnd_Structure.hIcon = NULL;
	wnd_Structure.hCursor = NULL;
	wnd_Structure.hbrBackground = GetSysColorBrush(colorBrush);
	wnd_Structure.lpszMenuName = NULL;
	wnd_Structure.lpszClassName = L"WindowClassName";
	wnd_Structure.hIconSm = LoadIcon(NULL,IDI_APPLICATION);	

	if( !RegisterClassEx( &wnd_Structure ) )
        return HWND(-1);

	hWnd = CreateWindowEx(WS_EX_CONTROLPARENT, L"WindowClassName", str_Title, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE | WS_SIZEBOX | WS_MAXIMIZEBOX, int_XPos, int_YPos, int_Width, int_Height, NULL, NULL, GetModuleHandle(NULL), game);

	return hWnd;
}
